﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2024-2025 LiLong                                              **
**  This file is part of OpenVisaApplication.                                   **
**                                                                              **
**  OpenVisaApplication is free software: you can redistribute it and/or modify **
**  it under the terms of the GNU General Public License as published by        **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  OpenVisaApplication is distributed in the hope that it will be useful,      **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU General Public License for more details.                                **
**                                                                              **
**  You should have received a copy of the GNU General Public License           **
**  along with OpenVisaApplication. If not, see <https://www.gnu.org/licenses/>.**
**********************************************************************************/
#include "StringEscape.h"

#include <tao/pegtl.hpp>
#include <tao/pegtl/contrib/unescape.hpp>

namespace AppCore
{
using namespace tao::pegtl;

struct EscapedX : seq<one<'x', 'X'>, rep<2, xdigit>>
{
};

using UnescapeKeys = one<'\'', '"', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'>;
struct UnescapeChar : public if_then_else<one<'\\'>, sor<EscapedX, UnescapeKeys>, ascii::any>
{
};
using EscapeKeys = one<'\'', '"', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v'>;
struct EscapeChar : sor<EscapeKeys, ascii::any>
{
};

using StringUnescapeRule = seq<star<UnescapeChar>, eof>;
using StringEscapeRule   = seq<star<EscapeChar>, eof>;

template<typename T>
struct Action
{
};

template<>
struct Action<EscapedX> : unescape::unescape_x
{
};

template<>
struct Action<UnescapeKeys> : unescape::unescape_c<UnescapeKeys, '\'', '"', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v'>
{
};

template<>
struct Action<ascii::any> : unescape::append_all
{
};

template<typename T, char... Rs>
struct EscapeC
{
    template<typename ActionInput>
    static void apply(const ActionInput& in, std::string& s)
    {
        assert(in.size() == 1);
        s += apply_one(in, static_cast<const T*>(nullptr));
    }
    template<typename ActionInput, char... Qs>
    [[nodiscard]] static std::string apply_one(const ActionInput& in, const one<Qs...>* /*unused*/)
    {
        static_assert(sizeof...(Qs) == sizeof...(Rs), "size mismatch between escaped characters and their mappings");
        return apply_two(in, { Qs... }, { Rs... });
    }

    template<typename ActionInput>
    [[nodiscard]] static std::string apply_two(const ActionInput& in,
                                               const std::initializer_list<char>& q,
                                               const std::initializer_list<char>& r)
    {
        const char c = *in.begin();
        for (std::size_t i = 0; i < q.size(); ++i)
        {
            if (*(q.begin() + i) == c)
            {
                return std::string("\\") + *(r.begin() + i);
            }
        }
        std::terminate(); // LCOV_EXCL_LINE
    }
};

template<>
struct Action<EscapeKeys> : EscapeC<EscapeKeys, '\'', '"', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'>
{
};

std::expected<QByteArray, QString> StringEscape::encode(const QString& source)
{
    std::string ret;
    if (!parse<StringUnescapeRule, Action>(string_input(source.toStdString(), ""), ret))
    {
        return std::unexpected(u8"字符串转义格式错误。");
    }
    return QByteArray::fromStdString(ret);
}

QString StringEscape::decode(const QString& source)
{
    std::string ret;
    if (!parse<StringEscapeRule, Action>(string_input(source.toStdString(), ""), ret))
    {
        return source;
    }
    return QString::fromStdString(ret);
}

} // namespace AppCore